﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Globalization;
using ExtryzeDLL.Blam.Scripting;

namespace ExtryzeDLL.Flexibility
{
    public class XMLOpcodeLookup : IOpcodeLookup
    {
        private Dictionary<ushort, string> _scriptTypeNameLookup;
        private Dictionary<ushort, ScriptValueType> _typeLookup;
        private Dictionary<ushort, string> _functionLookup;
        private Dictionary<short, string> _classLookup;

        public XMLOpcodeLookup(XDocument document)
        {
            XContainer root = document.Element("BlamScript");

            // Script execution types
            var scriptTypes = from element in root.Element("scriptTypes").Descendants("type")
                              select new
                              {
                                  Opcode = ParseUShortAttrib(element.Attribute("opcode")),
                                  Name = element.Attribute("name").Value
                              };
            _scriptTypeNameLookup = scriptTypes.ToDictionary(t => t.Opcode, t => t.Name);

            // Object types
            var objectTypes = from element in root.Element("objectTypes").Descendants("type")
                              select new
                              {
                                  Opcode = (short)ParseUShortAttrib(element.Attribute("opcode")),
                                  Name = element.Attribute("name").Value
                              };
            _classLookup = objectTypes.ToDictionary(t => t.Opcode, t => t.Name);

            // Value types
            var valueTypes = from element in root.Element("valueTypes").Descendants("type")
                             select new ScriptValueType(
                                 element.Attribute("name").Value,
                                 ParseUShortAttrib(element.Attribute("opcode")),
                                 ParseUShortAttrib(element.Attribute("size")),
                                 ParseBoolAttrib(element.Attribute("quoted"))
                             );
            _typeLookup = valueTypes.ToDictionary(t => t.Opcode);

            // Functions
            var functions = from element in root.Element("functions").Descendants("function")
                            select new
                            {
                                Opcode = ParseUShortAttrib(element.Attribute("opcode")),
                                Name = element.Attribute("name").Value
                            };
            _functionLookup = functions.ToDictionary(f => f.Opcode, f => f.Name);
        }

        public string GetScriptTypeName(ushort opcode)
        {
            string result;
            if (_scriptTypeNameLookup.TryGetValue(opcode, out result))
                return result;
            return null;
        }

        public ScriptValueType GetTypeInfo(ushort opcode)
        {
            ScriptValueType result;
            if (_typeLookup.TryGetValue(opcode, out result))
                return result;
            return null;
        }

        public string GetFunctionName(ushort opcode)
        {
            string result;
            if (_functionLookup.TryGetValue(opcode, out result))
                return result;
            return null;
        }

        public string GetTagClassName(short opcode)
        {
            string result;
            if (_classLookup.TryGetValue(opcode, out result))
                return result;
            return null;
        }

        private static ushort ParseUShortAttrib(XAttribute attrib)
        {
            if (attrib == null)
                return 0;
            string str = attrib.Value;
            if (str.StartsWith("0x"))
                return ushort.Parse(str.Substring(2), NumberStyles.HexNumber);
            return ushort.Parse(str);
        }

        private static bool ParseBoolAttrib(XAttribute attrib)
        {
            if (attrib == null)
                return false;
            return (attrib.Value.ToLower() == "true");
        }
    }
}
